﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAPICodePack.Sensors;
using System.Windows.Media.Media3D;

namespace SensorMayhem.Helpers
{
    public class AccelerometerReading
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }
    }


    public class AccelerometerSensorWithShakeDetection : IDisposable
    {
        private Accelerometer3D m_sensor;
        private event EventHandler m_ShakeDetectedHandler;
        private bool shaking = false;
        private double shakeThreshold = 0.7;
        private int shakeCount = 0;
        private AccelerometerReading m_lastReading;

        public AccelerometerSensorWithShakeDetection()
        {
            var sensors = SensorManager.GetSensorsByTypeId<Accelerometer3D>();
            if (sensors.Count > 0)
            {
                m_sensor = sensors.FirstOrDefault();
            }
            else
            {
                throw new NotSupportedException("sensor not supported on this device");
            }
        }

        public event EventHandler ShakeDetected
        {
            add
            {
                m_ShakeDetectedHandler += value;
                m_sensor.DataReportChanged += Default_ReadingChanged;
            }
            remove
            {
                m_ShakeDetectedHandler -= value;
                m_sensor.DataReportChanged -= Default_ReadingChanged;
            }
        }

        public void Start()
        {

            //if (m_sensor != null)
            //    m_sensor.Start();
        }

        public void Stop()
        {
            //if (m_sensor != null)
            //    m_sensor.Stop();
        }

        public SensorState State
        {
            get
            {
                return m_sensor.State;
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            //if (m_sensor != null)
            //    m_sensor.Dispose();
        }

        #endregion

        private void Default_ReadingChanged(Sensor sender, EventArgs e)
        {
            var a3d = new Acceleration3D(sender.DataReport);
            if (a3d != null)
            {
                AccelerometerReading reading = new AccelerometerReading() { X = a3d[AccelerationAxis.XAxis], Y = a3d[AccelerationAxis.YAxis], Z = a3d[AccelerationAxis.ZAxis] }; ;
                DateTimeOffset timeStamp = DateTime.Now;
                try
                {
                    if (m_lastReading != null)
                    {
                        if (!shaking && this.CheckForShake(m_lastReading, reading, shakeThreshold) && shakeCount >= 3)
                        {
                            //We are shaking
                            shaking = true;
                            shakeCount = 0;
                            OnShakeDetected();
                        }
                        else if (this.CheckForShake(m_lastReading, reading, shakeThreshold))
                        {
                            shakeCount++;
                        }
                        else if (!this.CheckForShake(m_lastReading, reading, 0.2))
                        {
                            shakeCount = 0;
                            shaking = false;
                        }
                    }
                    m_lastReading = reading;
                }
                catch { /* ignore errors */ }
            }
        }

        private void OnShakeDetected()
        {
            if (this.m_ShakeDetectedHandler != null)
                this.m_ShakeDetectedHandler(this, EventArgs.Empty);
        }

        private bool CheckForShake(AccelerometerReading last, AccelerometerReading current, double threshold)
        {
            var distance = Math.Sqrt(Math.Pow(current.X - last.X, 2) + Math.Pow(current.Y - last.Y, 2) + Math.Pow(current.Z - last.Z, 2));
            return distance > threshold;
        }
    }
}
